<!DOCTYPE html>
<title>Universal Default Justification Experiment</title>
<meta charset=utf-8>
<style id="renderStyle">
.space { }
.letter { }
</style>

<h1>Universal Default Justification Experiment</h1>

<p id="render"></p>

<form action="">
<label>Text to Justify: <textarea name=text id=text>서울특별시(서울特別市)는 한반도</textarea></label>
<label>Space Expansion Limit: <input id=cjk name=cjk value=0.67></label>
<!-- <label>Space-SEA Threshold: <input id=sea name=sea value=2.67></label> -->
<label>Split Hangul-Han: <input type=checkbox name=splitHangul id=splitHangul></label>
<ul>
<li class="button" style="color: green"  onclick="render()">Render!</li>
<li class="button" style="color: orange" onclick="save()" >Save!</li>
<li class="button" style="color: red"    onclick="window.location = window.location.href.match('^[^#\?]+')">Reset!</li>
</ul>
</form>

<style>
form { text-align: right; display: table; width: 100%; }
textarea { display: table-row; margin: auto; vertical-align: top; }
label { display: table-row; font-size: smaller; font-weight: bold; }
input, textarea { width: calc(96vw - 15rem); box-sizing: border-box; }
#render {
  box-shadow: 0.25em 0.25em 0.5em;
  border: solid 1px silver;
  min-height: 1em;
  max-width: 15em;
  margin: 1.5em auto 2em;
  white-space: nowrap;
  position: relative;
  font-size: 200%;
}
.button {
  font-weight: bold; cursor: pointer;
  border: solid; border-radius: 0.5em; padding: 0.1em 0.25em;
}
ul, li { margin: 1em 0; padding: 0; list-style: none; text-align: center; }
li { display: inline; }
</style>

<script>
'use strict';

var charCats = {
  // copied from http://fluxbb.org/forums/viewtopic.php?id=3866
  'space' :
    ' ' // U+0020
  ,
  'hangul' :
    '[\u1100-\u11FF' + // Hangul Jamo                          1100-11FF        (http://www.fileformat.info/info/unicode/block/hangul_jamo/index.htm)
     '\u3130-\u318F' + // Hangul Compatibility Jamo            3130-318F        (http://www.fileformat.info/info/unicode/block/hangul_compatibility_jamo/index.htm)
     '\uAC00-\uD7AF]'   // Hangul Syllables                     AC00-D7AF        (http://www.fileformat.info/info/unicode/block/hangul_syllables/index.htm)
  ,
  'han' :
    '[\u2E80-\u2EFF' + // CJK Radicals Supplement                2E80-2EFF        (http://www.fileformat.info/info/unicode/block/cjk_radicals_supplement/index.htm)
     '\u2F00-\u2FDF' + // Kangxi Radicals                        2F00-2FDF        (http://www.fileformat.info/info/unicode/block/kangxi_radicals/index.htm)
     '\u2FF0-\u2FFF' + // Ideographic Description Characters    2FF0-2FFF        (http://www.fileformat.info/info/unicode/block/ideographic_description_characters/index.htm)
     '\u3000-\u303F' + // CJK Symbols and Punctuation            3000-303F        (http://www.fileformat.info/info/unicode/block/cjk_symbols_and_punctuation/index.htm)
     '\u31C0-\u31EF' + // CJK Strokes                            31C0-31EF        (http://www.fileformat.info/info/unicode/block/cjk_strokes/index.htm)
     '\u3200-\u32FF' + // Enclosed CJK Letters and Months        3200-32FF        (http://www.fileformat.info/info/unicode/block/enclosed_cjk_letters_and_months/index.htm)
     '\u3400-\u4DBF' + // CJK Unified Ideographs Extension A    3400-4DBF        (http://www.fileformat.info/info/unicode/block/cjk_unified_ideographs_extension_a/index.htm)
     '\u4E00-\u9FFF]'  // CJK Unified Ideographs                4E00-9FFF        (http://www.fileformat.info/info/unicode/block/cjk_unified_ideographs/index.htm)
     // '\u20000-\u2A6DF' // CJK Unified Ideographs Extension B    20000-2A6DF    (http://www.fileformat.info/info/unicode/block/cjk_unified_ideographs_extension_b/index.htm)
  ,
  'kana' :
    '[\u3040-\u30FF]'        // Kana
};

function render() {
  var renderer = document.getElementById('render');
  var text = document.getElementById('text').value;
  var charList = [];
  for (c in text) {
    charList.push('<span>' + text[c] + '</span>');
  }
  renderer.innerHTML = charList.join('');
  document.getElementById('renderStyle').textContent = '';

  var splitHangul = document.getElementById('splitHangul').checked;
  var charList = renderer.childNodes;
  var numSpaces = 0; // number of spaces
  var numCJK = 0;    // number of stretchable gaps around CJK
  for (var c of charList) {
    var n = c.nextSibling;
    if (c.textContent.match(charCats['space'])) {
      c.setAttribute('class', 'space');
      numSpaces++;
      if (n &&
          n.textContent.match(charCats['han']) ||
          n.textContent.match(charCats['kana']) ||
          (n.textContent.match(charCats['hangul']) && !splitHangul)) {
        numCJK++;
      }
    }
    else {
      if (!n) break; // no space after last char
      if (c.textContent.match(charCats['han']) ||
          c.textContent.match(charCats['kana']) ||
          (c.textContent.match(charCats['hangul']) && !splitHangul)) {
        c.setAttribute('class', 'cjk');
        numCJK++;
      }
      else if (n.textContent.match(charCats['han']) ||
                n.textContent.match(charCats['kana']) ||
                (n.textContent.match(charCats['hangul']) && !splitHangul)) {
        numCJK++;
      }
    }
  }
  console.log(charList.length + ' chars: ' + numSpaces + ' spaces, ' + numCJK + ' cjk gaps');

  /* Calculate Space */
  var renderWidth = renderer.offsetWidth;
  renderWidth -= getComputedStyle(renderer).borderLeftWidth.slice(0,-2);
  renderWidth -= getComputedStyle(renderer).borderRightWidth.slice(0,-2);
  renderWidth -= getComputedStyle(renderer).paddingLeft.slice(0,-2);
  renderWidth -= getComputedStyle(renderer).paddingRight.slice(0,-2);

  var lastChar = charList[charList.length-1];
  var contentSize = lastChar.offsetLeft + lastChar.offsetWidth;

  var spaceToFill = renderWidth - contentSize;
  var x = spaceToFill;

  /* Split Space */
  var fontSize = getComputedStyle(renderer).fontSize.slice(0,-2);
  var cjkSpaceLimit = document.getElementById('cjk').value * fontSize;
  //var seaSpaceLimit = document.getElementById('sea').value * fontSize;
  
  var addToSpace = spaceToFill / numSpaces;
  if (addToSpace > cjkSpaceLimit)
    addToSpace = cjkSpaceLimit;
  spaceToFill -= addToSpace * numSpaces;
  
  var addToCJK = spaceToFill / (numCJK+numSpaces);
  console.log(renderWidth + ' - ' + contentSize + ' = ' + x + ' = ' + numSpaces + '*' + addToSpace + ' + ' + (numCJK+numSpaces) + '*' + addToCJK);

  document.getElementById('renderStyle').textContent = 
    '.space { padding-right: ' + (addToSpace+addToCJK) + 'px; }\n' +
    '.cjk { padding: 0 ' + addToCJK + 'px; }\n' +
    ':first-child, .cjk + .cjk { padding-left: 0; }'; 
}
</script>

<script>
/* Onload */
  /* Decode query and load into inputs */
  var query = window.location.hash.slice(1);
  if (!query) {
    query = window.location.href.match('\\?[^\\?]+');
  }
  if (query) {
    var pairs = decodeURIComponent(query[0].slice(1)).split('&&');
    for (var pair of pairs) {
      var [name, value] = pair.split('=');
      var field = document.getElementById(name);
      field.value = value;
      if (field.type == 'checkbox') {
        field.setAttribute('checked', 'checked');
      }
    }
  }
  /* Then render it*/
  render();

function save() {
  var pairs = [];
  var fields = document.getElementsByTagName('input');
  for (var i = 0; i < fields.length; i++) {
    if (fields[i].type == 'checkbox') {
      if (fields[i].checked)
        pairs.push(fields[i].id + '=' + fields[i].id);
    }
    else {
      pairs.push(fields[i].id + '=' + fields[i].value);
    }
  }
  // put this one last so URL is more hackable
  pairs.push('text=' + document.getElementById('text').value);
  var location = window.location.href.match('^[^#\?]+');
  window.location = location[0] + '?' + pairs.join('&&');
}
</script>
